Shader Theory
シェーダーを調べていて見つけた、そこはかとなくセオリーっぽいものを集めました。GLSL、Cg/HLSL、ShaderLabが混在してて内容にまとまりはありません。
TODO
分類してもくじを書く
HLSL/GLSLの分離
各項目で追記・修正したい箇所にTODOコメントを付記する
https://gyazo.com/6c4e53bb3465d9ddf45ab1bf696385f2.png
もくじ
数値と操作
色
トランスフォーム
図形
ポストエフェクト
https://gyazo.com/6c4e53bb3465d9ddf45ab1bf696385f2.png
定数
code:const.HLSL
// pi
// これ以外にも2πやπ / 2、π / 4などを定数として用意するのも有用
// UnityCG.cgincの組み込みでUNITY_PIというのがあるので、それを使えばよいです
// UNITY_TWO_PIとかもあるよ
// cgincはインストールディレクトリの/Editor/Data/CGIncludes/UnityCG.cgincにあるので読むと吉
// HDRPではPIが使えます
static float PI = 3.14159265359; // GLSLで書くならstaticをconstに置き換える
static float PI = acos(-1);
// タウ TAU = 2π
static float TAU 6.2831853071
// 黄金比 Golden Ratio
static float PHI = 1.618033988749894848;
// ガンマ 循環小数0.4545.. // 1/2.2, RECIPROCAL_GAMMA, 輝度=電圧出力率^1/2.2
static float GAMMA_CORRECTION = 0.45454545;
1よりも少しだけ小さな数字
code:BeneathOne.HLSL
cos(0.067) // 0.997756339504
0よりも少しだけ大きな数字
code:AboveZero.HLSL
cos(1.569) // 0.00179632582884
座標を色で見る
code:CoordColor.HLSL
// 座標(0, 0), (1, 0), (0, 1), (1, 1)の場所から他の場所にかけて、赤と緑の値の混合がグラデーション表示される
// シェーダーでのデバッグは色で見ると早道
return fixed4(i.uv, 0, 1);
周期を作る
三角関数に時間を与えると1.0と-1.0のあいだを行き来する値が出力される。
code:SinTime.HLSL
sin(_Time.y)
0.0から1.0の範囲の値をもとに三角関数で扱う場合、PIを掛け算すれば同様のことができる。
code:SinFromZeroToOne.HLSL
// v : 0 -> 1
// out : 0 -> 1 -> 0
float v = frac(_Time.y);
sin(v * UNITY_PI);
三角関数を0.0から1.0の範囲に正規化する
code:ZeroToOne.HLSL
// sin,cosの結果-1.0から1.0までの値を正の値0.0 - 1.0に置き換える
// ハーフランバートのライティングで使われる
sin(x) * 0.5 + 0.5
逆に0.0から1.0の範囲の値を-1.0~1.0の範囲に変換したい場合は、2倍して1を引けば良い。
code:MinusOneToOne.HLSL
// v : 0.0 - 1.0
2.0 * v - 1.0
時間を色で見る
はやさの違う時間経過が赤、緑、青それぞれの色の変わり方で表示される
code:TimeColor.HLSL
return fixed4(sin(_Time.xzy) * 0.5 + 0.5), 1);
灰色
与えられたひとつの小数点の値から灰色を作る。0、1を与えれば黒、白になる。
HLSLではfloatに対してSwizzleでv.xxxのように書くことができる。また、定数で(0).xxxのように書くこともできる。
同じことをGLSLで書いてもコンパイルエラーとなる。
code:Gray.HLSL
// Gray
fixed4 Gray(float c) {
return fixed4(c.xxx, 1); // swizzle
}
GLSLのgl_FragColor = vec4(vec3(c),1.0);をCg/HLSLで書くとこんなかんじ。
同様に赤。
code:Red.HLSL
// Red
fixed4 scgRed(float c) {
return fixed4(c, 0, 0, 1);
}
グレースケール
与えられたRGBの値からグレースケール化する。
code:Grayscale.HLSL
// float3 col
float g = (col.r + col.g + col.b) / 3.0;
輝度によるグレースケール
与えられたRGBの値から輝度を求める。
code:Luminance.HLSL
fixed4 c = tex2D(_MainTex, i.uv);
return 0.21 * c.r + 0.72 * c.g + 0.07 * c.b; // 輝度を求めるグレースケール化
// 上の掛け算は内積を使ってdot(c.rgb, fixed3(0.21, 0.72, 0.07))のようにも書ける
code:LuminanceBT601.HLSL
float luminance(float3 rgb)
{
return dot(rgb, float3(0.299, 0.587, 0.114)); // ITU-R BT.601
}
code:BT709.HLSL
color.rgb = dot(color.rgb, float3(0.2126, 0.7152, 0.0722));
時間経過で動かす
三角関数に時間を与えると周期のある値になる。
これを座標や色に掛けることで、動きを作ることができる。
code:SinTime.HLSL
float Timer()
{
return sin(_Time.y);
}
float HalfTimer()
{
return sin(_Time.y) * 0.5 + 0.5;
}
// はやさをパラメータで調整する
float HalfTimerTweak(float tweak)
{
return sin(_Time.y * tweak) * 0.5 + 0.5;
}
カラフルに変化させる。周期の違う時間をRGBそれぞれに与える。
code:Colorful.HLSL
// cには色を決める数値をセットしておく float c = 0.5
return fixed4(c * HalfTimerTweak(7), c * HalfTimerTweak(13), c * HalfTimerTweak(17), 1);
色をなんかいいかんじにする
RGBの数字を調整するのに疲れたら、hsvを使うといいかんじになるかもしれない。
hに座標と時間から求められる変数を与えて使う。明るさを増やしたい場合はvの数値を増やす。
最初はsとv1.0を与えて、後から0.1単位で増減させて調整する。
code:HSV.HLSL
vec3 hsv(float h,float s,float v){return ((clamp(abs(fract(h+vec3(0.,.666,.333))*6.-3.)-1.,0.,1.)-1.)*s+1.)*v;}
code:HUE.HLSL
// HUE
float3 hue(float3 v) {
return (abs(mod(v*6. + float3(0, 4, 2), 6.) - (3).xxx) - 1.)*0.5 + 0.3;
}
// defineして利用する場合
// TODO: write macro
//#define hue(v) (float3(1),(abs(mod(v*6.+float3(0,4,2),6.)-3.)-1.)*0.5+0.3)
t秒ごとにn個のパターンを繰り返す
t秒毎にパターンを切り替える時間の扱い。modとfloorを組み合わせればパターンの繰り返しが実現できる。
code:Pattern.HLSL
// パターンは4つ。0.5秒毎にパターンが0→1→2→3→0と切り替わる。
float tmod = fmod(_Time.y, 0.5 * 4);
float pat4 = floor(0.5 * 4 * tmod);
code:PatternInt.HLSL
// 距離関数のペア
// 1秒ごとに4つのパターンをあらわす
int pair = int(floor(fmod(_Time.y, 4.0)));
// t秒ごとにn個のパターンを繰り返す
int pair = int(floor(fmod(_Time.y, n * t) / t));
トランスフォーム
平行移動
code:Translate.HLSL
// 座標の足し算。float2 p。
p + float2(0.2, -0.6)
拡大・縮小
code:Scale.HLSL
mul(float2x2(x, 0, 0, y), float2(u, v))
回転
code:Rotate.HLSL
// 回転。float2 p。
float2(p.x * cos(a) - p.y * sin(a), p.x * sin(a) + p.y * cos(a))
// 回転行列を掛ける場合
mul(f2x2, p2)
// GLSLと違ってf2x2 * p2と書くことはできない
3Dを回す
code:Rotate3D.HLSL
// 2つの軸を指定すれば、2x2の回転行列で回すことが実現できる
float3 p = float3(uv, -10);
float t = _Time.y;
p.xz = mul(float2x2(cos(t), -sin(t), sin(t), cos(t)), p.xz);
タンジェントスペースを使って3Dを3次元の回転で扱うこともできる。法線(normal)と接面(tangent)がわかっていることが前提。法線と接面の外積からバイタンジェントが出せるので、それぞれ直交するこれら3つで3次元座標の変換を扱うことができるようになる。
code:TangentSpace.HLSL
// float3 p。vは法線ベクトルとタンジェントを含む構造体。
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
float3x3 rotation = float3x3(v.tangent.xyz , binormal, v.normal)
p = mul(rotation, p);
// もし法線のみがわかっていてタンジェントがない場合でも、(0,1,0)とcrossして仮のタンジェントを、法線と仮タンジェントをcrossしてバイタンジェントを、最後に法線とバイタンジェントをcrossしてタンジェントを出す…みたいなやり方ができるハズ
code:RotateQuaternion.HLSL
float3 rotate(float3 p, float3 v, float a)
{
float4 q = float4(sin(a / 2.) * v, cos(a / 2.)); // quaternion
return cross(cross(p, q.xyz) - q.w * p, q.xyz) * 2. + p;
}
任意の軸周りでの回転
https://gyazo.com/176841eb7788cc100218263b4adf31ff
xyzいずれかの軸回りであればこう。
code:rot3D.GLSL
vec3 rotX(vec3 p,float a){return vec3(p.x,p.y*cos(a)-p.z*sin(a),p.y*sin(a)+p.z*cos(a));}
vec3 rotY(vec3 p,float a){return vec3(p.x*cos(a)-p.z*sin(a),p.y,p.x*sin(a)+p.z*cos(a));}
vec3 rotZ(vec3 p,float a){return vec3(p.x*cos(a)-p.y*sin(a),p.x*sin(a)+p.y*cos(a),p.z);}
クォータニオン/Quaternion
code:Quaternion.HLSL
float3 rotate(float3 p,float3 axis,float theta){
float3 v=cross(axis,p),u=cross(v,axis);
return u*cos(theta)+v*sin(theta)+axis*dot(p,axis);
}
クォータニオンと同次座標(eg;3次元の座標を変換するために4次元の正方行列を使う)は別物。
ディスタンスファンクション
length(p)。あらゆるすべての基本となる関数。主にレイマーチングの説明で出てくる用語だけど、基本的な図形を描くのに使われる。
code:DistanceFunction.HLSL
// 球。pは空間の一点を指すベクトル。sは半径を指す数字。
length(p) - s;
// 直線。a,bは始点と終点。rは線(カプセル)の半径。
float3 pa = p-a, ba = b-a;
float h = clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0);
length(pa-ba*h)-r;
ディスタンスフィールド
フォント、テクスチャ画像をベクトル化して、拡大・縮小しても潰れないように扱う用途で使われる。
極座標
code:Atan2.HLSL
float a = atan2(uv.y, uv.x);
歪み
縦・横のもう一方の成分を与えると歪む。
code:Distortion.HLSL
// float2 p = i.uv
p.x += frac(5.0 * p.y);
繰り返し
UVへの掛け算での繰り返し表示となる。
code:UVMultipleHLSL
// float2 p = i.uv
p = frac(5.0 * p);
時間周期での動きの繰り返し。
code:PxBiSinTimeHLSL
// float2 p = i.uv
p.x *= sin(_Time.y);
modを使う繰り返し。
code:ModRepeat.HLSL
// fmodは負の数をうまく扱えないので、mod関数を追加する。自分用のcgincを用意しておくと便利。
float mod(float x, float y)
{
return x - y * floor(x / y);
}
float2 mod(float2 x, float2 y)
{
return x - y * floor(x / y);
}
float3 mod(float3 x, float3 y)
{
return x - y * floor(x / y);
}
float4 mod(float4 x, float4 y)
{
return x - y * floor(x / y);
}
float rep(float p, float d) {
return mod(p - d * .5, d) - d * .5;
}
// ディスタンスファンクションで球や箱を繰り返し表示するのに利用するmod
// float3 p = float3(i.uv, 1.0)
float3 rep(float3 p, float d) {
return mod(p - d * .5, d) - d * .5;
}
code:moda.GLSL
vec2 moda(vec2 p,float per){
float a=atan(p.y,p.x);
float l=length(p);
a=mod(a-per/2.,per)-per/2.;
return vec2(cos(a),sin(a))*l;
}
疑似乱数、ノイズ関数
さまざまな乱数、ノイズが存在する。人の眼に自然に見えるパーリンノイズやsnoise(シンプレックスノイズ)などが重宝される。詳しくはNoise, Randを参照。 code:FractSin.HLSL
// ゲーム界隈に伝統的に伝わる有名なノイズ。GPUのアーキテクチャに依存する浮動小数点誤差が発生し、環境による動作の違いがあり現在では利用は推奨されない。
float rand(vec2 co){
frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
}
code:rnd.GLSL
float rnd(float t) {
return fract(sin(t*789.451)*7541.223);
}
float rnd(vec2 t) {
return fract(dot(sin(t*vec2(789.451)+t.yx*vec2(842.544)),vec2(7541.223)));
}
code:HashWithoutSine.GLSL
// Hash without Sine
// Creative Commons Attribution-ShareAlike 4.0 International Public License
// Created by David Hoskins.
/// 3 out, 2 in...
vec3 hash32(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yxz+33.33);
return fract((p3.xxy+p3.yzz)*p3.zyx);
}
Remap
ある範囲の値を別の範囲で線形に置き換える。
code:remap.GLSL
#define saturate(x) clamp(x, 0.0, 1.0) float remap(float val, float inMin, float inMax, float outMin, float outMax)
{
return saturate(outMin + (val - inMin) * (outMax - outMin) / (inMax - inMin));
}
vec2 remap(vec2 val, float inMin, float inMax, float outMin, float outMax)
{
return vec2(remap(val.x, inMin, inMax, outMin, outMax), remap(val.y, inMin, inMax, outMin, outMax));
}
vec3 remap(vec3 val, float inMin, float inMax, float outMin, float outMax)
{
return vec3(remap(val.x, inMin, inMax, outMin, outMax), remap(val.y, inMin, inMax, outMin, outMax), remap(val.z, inMin, inMax, outMin, outMax));
}
UVスクロール
テクスチャを時間経過で動かして、疑似的に動いている表現を作り出す。水面、雲、ベルトコンベアみたいなものに使える。
code:UVScroll.HLSL
uv += sin(_Time.x);
code:UVScrollFract.HLSL
//0.0 - 1.0の範囲外だとテクスチャがサンプルされない処理系の場合はfractする
uv += sin(frac(time));
色の加算と乗算
色は0.0-1.0の範囲で表現される。そのため、色と色を掛け算すると値がゼロに近づき、だんだん暗くなる。
色を混ぜ合わせる時に明るくしたい場合には加算で色を処理する。
加算合成シェーダー
エフェクトを作成する場合、複数のパーティクルが重なったところで明るさが強くなるルックを実現したい場合がある。
Unity標準の組み込みシェーダーでは、ちょうどいい加算合成シェーダーがないため、自分用のシェーダーを用意しておくと良い。なお、照明の明るさが強い時に画面全体が白焼けするのを避けるには、HDRを利用する(典型的な利用シーンはPost Processingを使ったもの)。
code:BlendAdd.HLSL
fixed4 col = i.color * _TintColor * tex2D(_MainTex, i.texcoord);
col.rgb += pow(col.rgb, _LightBoostPow)*_LightBoostScale * col.a;
//col.rgb += lerp(0, 1, min(1, (col.r + col.g + col.b)));
col.a += _Wiggle*sin(_Time.x*2048);
col.a = saturate(col.a);
col.rgb *= 0.5;
return col;
事前乗算アルファ合成
https://gyazo.com/b5e55a9c8d4b86e65d8e7ecd87ae2f4a
HDR
従来の色の表現はRGBそれぞれ最大1.0の値で扱っていたが、この解像度を上げるものがHDR。
「すごく明るい」状態をRGBすべて1.0にすると画面が真っ白にしかならないけれど、HDRで1.0より大きな数字を使えるようにすれば画面が潰れずに明るい場所をまぶしく見せることが可能になる。
https://gyazo.com/d1f40a05d2135119ea88350e01872b15
ガンマ補正
TODO: 0.454545..ガンマ、sRGBといったあたりの話をもうちょっと詳しく
循環小数0.454545.. = 1/2.2を使う。
電圧の数字と、人の目に見える色の数字は線形の関係ではない。これを変換する数字が0.454545..という循環小数。
近年ではテクスチャ、シェーダー、映像処理などでの作業工程ではガンマ補正を行わず、最後のレンダリングでのみガンマ補正を行うというリニアワークフローという手法がある。
Unityで部屋の天井隅の影が不自然だったり、照明があたった壁が妙に明るく白焼けしているのは、リニアワークフローができていないケース。
Vertex Texture Fetch(VTF)
頂点シェーダーでテクスチャサンプリングを行う。VTFはテクスチャで任意のデータを扱う手法。
Vertex Displacement Mapping
code:VertexDisplacementMapping.HLSL
float4 Displacement( float4 Position, float3 Normal, float2 TexCoord )
{
float4 result = Position;
result.xyz = Position.xyz
+ Normal // 法線
* (tex2Dlod( DisplacementSmp, float4(TexCoord, 0, 1) ).x ) // tex2Dだとだめなので仕方なくtex2Dlod
* Scale; // 高さをスケーリング
return result;
}
if文はstep()で置き換えられる
trueを1、falseを0として扱う。
code:IfStep.HLSL
// if文
if(x <= 0.1) {
return 1.0;
} else {
return 0.0;
}
// step()
return step(x, 0.1);
整数と小数に分解する
123.456のような数字から、floorで123、fracで0.456を取り出す。
code:DivModOne.HLSL
// float a
float i = floor(a);
float d = frac(a);
バリューノイズ、ボロノイノイズ(Voronoi)ではこの式がそのまま使われる。 線を引く
シェーダーは他のドロー環境と違い、線分を描くのは実はたやすくない。
内積から領域を判断する手法で実装する。
code:Line.HLSL
float crs(float2 v1, float2 v2) {
return v1.x*v2.y - v1.y*v2.x;
}
// 2点v1, v2を端点に持つ線分との距離を返す
float line(float2 p, float2 v1, float2 v2) {
p -= v1;
float2 v = v2-v1;
float t = dot(p, normalize(v));
if (t<0.0) {
return length(p);
} else if (t>length(v)) {
return length(p-v);
} else {
return abs(crs(p, normalize(v)));
}
}
code:Line2.HLSL
// Line
// 直線。a,bは始点と終点。rは線(カプセル)の半径。
float3 pa = p-a, ba = b-a;
float h = clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0);
length(pa-ba*h)-r;
円
code:Circle.HLSL
// draw round
// to call
// float _Rad;
// float c = Round2(i.uv, _Rad);
// return Gray(c);
float Round2(float2 uv, float rad)
{
float2 st = uv - 0.5;
return step(distance(st, 0), rad);
}
// distanceはシンプルにlengthで書くことができる
step(length(p), 0.5)
四角形
code:Rectangle.HLSL
// Rectangle
// to call
// return float4(Rectangle(i.uv, 0.3).xxx, 1);
float Rectangle(float2 uv, float size) {
float2 st = uv - 0.5;
return step(max(abs(st.x), abs(st.y)), size);
}
三角形
https://gyazo.com/b37df87fe92f780b451d6d76e0a76c16
code:Triangle.HLSL
// Triangle
// 極座標で領域を判断する三角形の実装
// to call
// return float4(Triangle(i.uv).xxx, 1);
float Triangle(float2 uv){
float2 st = 2 * (uv - 0.5);
// Number of sides of your shape
int N = 3;
// Angle and radius from the current pixel
float a = atan2(st.y, st.x) + PI;
float r = 2 * PI / float(N);
// Shaping function that modulate the distance
float d = cos(floor(0.5 + a / r) * r - a) * length(st);
return 1.0 - smoothstep(0.4, 0.41, d);
}
TODO:任意の多角形を描く関数。HLSLに書き直す。
code:GLSL
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st.x *= u_resolution.x/u_resolution.y;
vec3 color = vec3(0.0);
float d = 0.0;
// Remap the space to -1. to 1.
st = st *2.-1.;
// Number of sides of your shape
int N = 3;
// Angle and radius from the current pixel
float a = atan(st.x,st.y)+PI;
float r = TWO_PI/float(N);
// Shaping function that modulate the distance
d = cos(floor(.5+a/r)*r-a)*length(st);
color = vec3(1.0-smoothstep(.4,.41,d));
// color = vec3(d);
gl_FragColor = vec4(color,1.0);
}
ハート
code:Heart.HLSL
// Heart
float Heart(float2 p) {
float2 st = float2(2, 2.6) * p - 1;
return step(st.x * st.x + pow(st.y - sqrt(abs(st.x)), 2), 0.7);
}
https://gyazo.com/23af8fc39f99c169e1ca6e153948544f
モーフィング
ある図形から別の図形にモーフィングさせたい場合は、lerp関数に2つの図形を与えて_Timeで動かす。
code:Morph.HLSL
float Morph(float a, float b) {
return lerp(a, b, sin(_Time.y) * 0.5 + 0.5);
}
GLSLならlerpのかわりにmixを使う。
code:Morph2.HLSL
fixed4 frag (v2f i) : SV_Target
{
float t = Triangle(i.uv);
float h = Heart(i.uv);
return Gray(Morph(t, h));
}
https://gyazo.com/6df2f747112d1ad041a698855ccd72a3
ディスタンスファンクションでのモーフィング
code:DistanceFunctionMorph.HLSL
// ディスタンスファンクションの返り値に閾値も渡す
float2 circle(float2 p) {
return float2(length(p), 0.7);
}
float2 square(float2 p) {
return float2(abs(p.x) + abs(p.y)), 0.5);
}
// 距離と閾値の両方をまとめて補完する
float3 col = lerp(circle(p), square(p), sin(_Time.y));
smooth minimum
モーフィングを単純に実装すると変化がガタつくので、計算方法に手を入れる。スムース化するにはexpを使うが、他にもいくつか方法がある。
以下の関数で、a,bはそれぞれ違う図形を指す。
code:SmoothMinimum.HLSL
// exponential smooth, k=32
float smin( float a, float b, float k )
{
float res = exp2( -k * a ) + exp2( -k*b );
return -log2( res ) / k;
}
// polynomial smooth, k=0.1
float smin( float a, float b, float k )
{
float h = clamp( 0.5 + 0.5 * (b-a) / k, 0.0, 1.0 );
return lerp( b, a, h ) - k * h * (1.0-h);
}
// power smooth, k=8
float smin( float a, float b, float k )
{
a = pow( a, k ); b = pow( b, k );
return pow( (a * b) / (a + b), 1.0 / k );
}
モザイクエフェクト
code:Mosaic.HLSL
// Mosaic
float2 Mosaic(float2 uv, float mul)
{
float m = mul * HalfTimer();
uv = floor(uv * m) / m;
return uv;
}
// Smooth Mosaic
// 一定の範囲で周辺ピクセルからカラーピックして平滑化するモザイク
fixed4 SmoothMosaic(sampler2D tex, float2 uv, float mul)
{
float d1 = 1 / mul;
float d2 = 1 / mul / mul;
float2 xy = Mosaic(uv, mul);
fixed4 col = fixed4(0, 0, 0, 0);
for (float i = 0.0; i < d1; i += d2) {
for (float j = 0.0; j < d1; j += d2) {
col += tex2D(tex, float2(xy.x + i, xy.y + j));
}
}
return col * d2;
}
RGBシフト
code:HLSL
fixed4 frag (v2f i) : SV_Target
{
float t = _Time.y;
fixed4 col = tex2D(_MainTex, i.uv);
col.x = tex2D(_MainTex, i.uv + float2(0.1 * abs(sin(t)), 0));
col.z = tex2D(_MainTex, i.uv - float2(0.1 * abs(sin(t)), 0));
return col;
}
集中線
https://gyazo.com/0273ea0629164e3127778acadd3db61f
水滴
https://gyazo.com/d3ca1bdf5e2dcef27981255ba0479997
イージング
時間で動きや色を変化させる時に線形補完だとイマイチな場合、動きに表情をつけたい場合はイージング関数を使う。
https://gyazo.com/68328022b90c6d23150b07c57d2239f7
ハーフランバート
ランバートはライトのベクトルとマテリアルの法線ベクトルで内積を使って行うライティング。ディフューズを求める。
内積はcos関数で-1.0から+1.0の範囲で値が出てくるが、これを0.0から1.0の範囲に置き換えることで、物理的には嘘だけどそれっぽく見えるようになるという手法。ValveのTeamFortressで使われた。
code:HLSL
float diffuseLight = max(dot(L, N), 0) * 0.5 + 0.5;
float3 diffuse = Kd * lightColor * (diffuseLight * diffuseLight);
フォン
フォンはハーフベクトルとマテリアルの法線ベクトルで内積を使って行うライティング。スペキュラーを求める。
Unityのシェーダーではブリン・フォンというライティング名称で利用できる。
code:Phong.HLSL
float specular = 0.0;
float specAngle = max(dot(reflectDir, viewDir), 0.0);
specular = pow(specAngle, 4.0);
パララックス
いろいろなグラフ
code:formula
x*x+pow(y−sqrt(abs(x))), 2)=1
時間から座標を決める
code:HLSL
float beat = _Time.y * 128.0 / 60.0;
flaot3 st = float3(cos(beat), sin(beat), beat - 1.0);
dot(p, p)
|p| * |p| * 1.0
地面に落ちた君の影
code:HLSL
dot(you, float3(0.0, 1.0, 0.0))
コストの安い法線算出
code:HLSL
float3 normal(float3 pos) {
float2 e = float2(1.0, -1.0) * 0.5773 * 0.0001;
return normalize(e.xyy * map(pos + e.xyy) +
e.yyx * map(pos + e.yyx) +
e.yxy * map(pos + e.yxy) +
e.xxx * map(pos + e.xxx));
}